/*
Copyright (c) 2009 Ben Beaumont

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. 
*/
package com.dogcatfishdish.utils
{
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	
	/**
	 * Set of helpers to create basic bitmap patterns. 
	 * <p>usage:</p>
	 * <pre>var bitmapData:BitmapData = PatternUtils.Grid(800, 600);
	 * var bitmap:Bitmap = new Bitmap(bitmapData);
	 * addChild(bitmap);</pre> 
	 *  
	 * @author benbeaumont
	 * 
	 */	
	public class PatternUtils
	{
		
		static public const STRIPE_ANGLE_0:int = 999;
		static public const STRIPE_ANGLE_45:int = 1;
		static public const STRIPE_ANGLE_90:int = 0;
		static public const STRIPE_ANGLE_135:int = -1;
		
		/**
		 * Creates a checkerboard pattern BitmapData.
		 * 
		 * @param width Width of the BitmapData.
		 * @param height Height of the BitmapData.
		 * 
		 * @param cellWidth Width of the checker cells. A value of 0 creates nothing, and can therefore be use to create lines. Default is 1.
		 * @param cellHeight Height of the checker cells. A value of 0 creates nothing, and can therefore be use to create lines. Default is 1.
		 * 
		 * @param foregroundColor Hex color value. Default is 0x000000.
		 * @param foregroundAlpha Alpha of the foreground, between 0 and 1. Default is 1.
		 * @param backgroundColor Hex color value. Default is 0x000000.
		 * @param backgroundAlpha Alpha of the background, between 0 and 1.
		 * 
		 * @return BitmapData
		 */			
		static public function Checkerboard(width:uint, 
											height:uint, 
											cellWidth:uint = 1, 
											cellHeight:uint = 1,
											foregroundColor:uint = 0, 
											foregroundAlpha:Number = 1,
											backgroundColor:uint = 0, 
											backgroundAlpha:Number = 0):BitmapData {
												
			var bmpd:BitmapData = new BitmapData(width, height, true, 0x00000000);
			
 			var val:uint, val2:uint, col:uint, i:uint, j:uint;
			
			var xRatio:Number = 1 / cellWidth;
			var yRatio:Number = 1 / cellHeight;

			var a:uint = uint(foregroundAlpha * 0xFF)<<24 | foregroundColor;
			var b:uint = uint(backgroundAlpha * 0xFF)<<24 | backgroundColor;
			
			var byteArray:ByteArray = new ByteArray();
			
			var xTotal:uint = cellWidth * 4;
			var yTotal:uint = cellHeight * 4;

			for(i = 0; i < yTotal; ++i)
			{
				val = uint(((i * yRatio) | 0) % 2);

				for(j = 0; j < xTotal; ++j)
				{
					val2 = uint(((j * xRatio) | 0) % 2);
					byteArray.writeUnsignedInt(((val ^ val2) == 0) ? b : a);
				}	
			}
			
			var divX:uint = uint(((width / xTotal) | 0) + 1);//number of sample per width
			var divY:uint = uint(((height / yTotal) | 0) + 1); //number of samples per height

			var rect:Rectangle = new Rectangle(0, 0, xTotal, yTotal);//create a rectangle to map the sample across the bitmap
			var bp:BitmapData = new BitmapData(divX * xTotal, divY * yTotal, true, 0x00000000);
			
			// copy pixels to the bitmapdata
			for(i = 0; i < divX; ++i)
			{
				rect.x = xTotal * i;
				
				for(j = 0; j < divY; ++j)
				{
					rect.y = yTotal * j;

					byteArray.position = 0;
					bp.setPixels(rect, byteArray);
				}
			} 
			
			bmpd.copyPixels(bp, bmpd.rect, new Point(0, 0));
			
			bp.dispose();
 
			return bmpd;
		}
		
		/**
		 * Create a grid pattern BitmapData.
		 * 
		 * @param width Width of the BitmapData.
		 * @param height Height of the BitmapData.
		 * 
		 * @param cellWidth Pixel width of the cells. Default value is 10.
		 * @param cellHeight Pixel height of the cells. Default value is 10.
		 * @param lineWidth Width of the grid lines. Default value is 1.
		 * @param lineHeight Height of the grid lines. Default value is 1.
		 * 
		 * @param gridColor Hex color value for the grid lines. Default is 0x000000
		 * @param gridAlpha Alpha for the grid lines. Number value from 0 to 1. Default is 1.
		 * @param backgroundColor Hex color value for the background color. Default is 0x000000
		 * @param backgroundAlpha Alpha for the background. Number value from 0 to 1. Default is 0.
		 * 
		 * @return BitmapData.
		 * 
		 */		
		static public function Grid(width:uint, 
									height:uint, 
									cellWidth:uint = 10,
									cellHeight:uint = 10,
									lineWidth:uint = 1,
									lineHeight:uint = 1,
									gridColor:uint = 0, 
									gridAlpha:Number = 1,
									backgroundColor:uint = 0,
									backgroundAlpha:Number = 0):BitmapData{
										
			var bmpd:BitmapData = new BitmapData(width, height, true, 0x00000000);
			
			var val:uint, val2:uint, col:uint, i:uint, j:uint;

			var xTotal:uint = cellWidth + lineWidth;
			var yTotal:uint = cellHeight + lineHeight;			

			var a:uint = uint(gridAlpha * 0xFF)<<24 | gridColor;
			var b:uint = uint(backgroundAlpha * 0xFF)<<24 | backgroundColor;
			
			var byteArray:ByteArray = new ByteArray();
			
			for(i = 0; i < yTotal; ++i)
			{
				val = uint(i % yTotal);

				for(j = 0; j < xTotal; ++j)
				{
					val2 = uint(j % xTotal);
					byteArray.writeUnsignedInt((val >= lineHeight && val2 >= lineWidth) ? b : a);
				}
			}
			
			var divX:uint = uint(((width / xTotal) | 0) + 1);//number of sample per width
			var divY:uint = uint(((height / yTotal) | 0) + 1); //number of samples per height

			var rect:Rectangle = new Rectangle(0, 0, xTotal, yTotal);//create a rectangle to map the sample across the bitmap
			
			var bp:BitmapData = new BitmapData(divX * xTotal, divY * yTotal, true, 0x00000000);
			
			// copy pixels to the bitmapdata
			for(i = 0; i < divX; ++i)
			{
				rect.x = xTotal * i;
				
				for(j = 0; j < divY; ++j)
				{
					rect.y = yTotal * j;

					byteArray.position = 0;
					bp.setPixels(rect, byteArray);
				}
			} 
			
			bmpd.copyPixels(bp, bmpd.rect, new Point(0, 0));
			
			bp.dispose();

			return bmpd;
		}
	
		/**
		 * Creates a stripe pattern. Generally used for 45 degree stripes, but this can be changed using the direction parameter.
		 * 
		 * @param width Width of the BitmapData
		 * @param height Height of the BitmapData
		 * 
		 * @param cellWidth Pixel distance between lines. Default is 2.
		 * @param lineWidth Pixel width of lines. Default is 2.
		 * @param direction int defining the direction/angle of the stripes. 
		 * Usually STRIP_ANGLE_45 or STRIPT_ANGLE_135 which correspond to 1 and -1 respectively.
		 * Other values can be used but the results are unpredicatable, though fun. Default is STRIPE_ANGLE_45
		 * 
		 * @param lineColor Hex value for the line color. Defaults to 0x000000
		 * @param lineAlpha Number from 0 to 1 for the line alpha. Defaults to 1.
		 * @param backgroundColor Hex colour value for the background. Defaults to 0x000000
		 * @param backgroundAlpha Number from 0 to 1 for the background alpha. Defaults to 0.
		 * 
		 * @return BitmapData
		 */		
		static public function Stripes(width:uint, 
									height:uint, 
									cellWidth:uint = 2, 
									lineWidth:uint = 2,
									direction:int = 1,
									lineColor:uint = 0,
									lineAlpha:Number = 1,
									backgroundColor:uint = 0,
									backgroundAlpha:Number = 0):BitmapData {
			
			
			
			var i:uint, j:uint, val:int, val2:int;
			
			var adjustedWidth:int = int(width * direction);
			
			var xTotal:uint = cellWidth + lineWidth; //sample size
			var xTotalDouble:uint = xTotal * 2; //double it to improve and balance performance across cell sizes
			
			var bitmapData:BitmapData = new BitmapData(width, height, true, 0x00000000);//create the main data container.
			var byteArray:ByteArray = new ByteArray();//create a byteArray to store a sample to repeat
	
			var a:uint = uint(backgroundAlpha * 0xFF)<<24 | backgroundColor; //set background colour as 32bit
			var b:uint = uint(lineAlpha * 0xFF)<<24 | lineColor; //set line colour as 32 bit
			
			// create the sample
			for(i = 0; i < xTotalDouble; ++i)
			{
				val = int(i * direction);
				
				if (direction == STRIPE_ANGLE_0) 
				{
					val2 = i % xTotal;

					for(j = 0; j < xTotalDouble; ++j)
						byteArray.writeUnsignedInt((val2 < cellWidth) ? a : b);
					
					continue;
				}
				else
				{
					for(j = 0; j < xTotalDouble; ++j)
					{
						val2 =	adjustedWidth + j + val;
						val2 = (val2 < 0) ? -val2 : val2;		
						val2 = val2 % xTotal;
							
						byteArray.writeUnsignedInt((val2 < cellWidth) ? a : b);
					}	
				}				
			}
			
			var divX:uint = uint(((width / xTotalDouble) | 0) + 1);//number of sample per width
			var divY:uint = uint(((height / xTotalDouble) | 0) + 1); //number of samples per height
			
			var rect:Rectangle = new Rectangle(0, 0, xTotalDouble, xTotalDouble);//create a rectangle to map the sample across the bitmap
			var bp:BitmapData = new BitmapData(divX * xTotalDouble, divY * xTotalDouble, true, 0x00000000);
			
			// copy pixels to the bitmapdata
			for(i = 0; i < divX; ++i)
			{
				rect.x = xTotalDouble * i;
				
				for(j = 0; j < divY; ++j)
				{
					rect.y = xTotalDouble * j;

					byteArray.position = 0;
					bp.setPixels(rect, byteArray);
				} 
			}
			bitmapData.copyPixels(bp, bitmapData.rect, new Point(0, 0));
			
			bp.dispose();
			
			return bitmapData;
		}
		
		
		/**
		 * Takes an input BitmapData object and applys as a pattern in a new BitmapData object.
		 *  
		 * @param width Width of the output BitmapData object.
		 * @param height Height pf the output BitmapData object
		 * @param pattern The BitmapData object to use as the pattern.
		 * 
		 * @return BitmapData object 
		 * 
		 */		
		static public function PatternFill(width:uint, 
										   height:uint, 
										   pattern:BitmapData):BitmapData {
			//TODO add in color changing parameters, restict it to black and white patterns.
			
			var bitmapData:BitmapData = new BitmapData(width, height, true, 0x00000000);//create the main data container.
			
			var divX:uint = uint(((width / pattern.width) | 0) + 1);
			var divY:uint = uint(((height / pattern.height) | 0) + 1);
			
			var rect:Rectangle = new Rectangle(0, 0, pattern.width, pattern.height);
			var point:Point = new Point(0, 0);
			
			var i:uint = 0;
			var j:uint = 0;
			
			for(i = 0; i < divX; ++i)
			{
				point.x = pattern.width * i;
				
				for(j = 0; j < divY; ++j)
				{
					point.y = pattern.height * j;

					bitmapData.copyPixels(pattern, rect, point); //TODO add in alpha bitmap extras
				} 
			}
			return bitmapData;
		}
		
	
	}
}